// Tierless
// Copyright © 2010 Alphora
// This file is licensed under a modified BSD-license (see tierless_license.txt).

using System;

using Tierless.Framework.Data;
using Tierless.Framework.Entities;

namespace Tierless.Framework.Constraints
{
	/// <summary>
	/// Represents the constraint evaluation context used during constraint validation.
	/// </summary>
	public class ConstraintContext : EntityContext
	{
		/// <summary>
		/// Constructs a new instance of a ConstraintContext.
		/// </summary>
		/// <param name="entityAccessor">The entity accessor used to allow constraints to access other entities.</param>
		/// <param name="entityChange">The entity change being validated.</param>
		/// <param name="entities">The current set of entities being validated.</param>
		public ConstraintContext(IEntityAccessor entityAccessor, EntityChange entityChange, EntitySets entities)
			: base(entityAccessor, entities)
		{
			FEntityChange = entityChange;
		}

		private readonly EntityChange FEntityChange;

		/// <summary>
		/// Gets the entity change being validated.
		/// </summary>
		public EntityChange EntityChange
		{
			get { return FEntityChange; }
		}

		/// <summary>
		/// Gets the entity being validated.
		/// </summary>
		public Entity Entity
		{
			get { return FEntityChange.Entity; }
		}

		/// <summary>
		/// Gets the transition represented by the change.
		/// </summary>
		/// <remarks>
		/// If the entity is being added, the transition is <c>Insert</c>.
		/// If the entity is being deleted, the transition is <c>Delete</c>.
		/// Otherwise, the transition is <c>Update</c>.
		/// </remarks>
		public ConstraintTransition Transition
		{
			get
			{
				return
					FEntityChange.Entity.IsNew
						? ConstraintTransition.Insert
						: FEntityChange.Entity.IsDeleted
							? ConstraintTransition.Delete
							: ConstraintTransition.Update;
			}
		}

		/// <summary>
		/// Gets or sets the set of column names affected by the change.
		/// </summary>
		public string[] ColumnNames { get; set; }

		/// <summary>
		/// Gets or sets the severity of the violation.
		/// </summary>
		public ViolationSeverity Severity { get; set; }

		/// <summary>
		/// Gets or sets the message for the violation.
		/// </summary>
		public string Message { get; set; }

		/// <summary>
		/// Gets the value of the given column for the given entity.
		/// </summary>
		/// <param name="entity">The entity to use for the retrieval.</param>
		/// <param name="columnName">The name of the column to retrieve.</param>
		/// <returns>The value of the column for the entity.</returns>
		/// <exception cref="ArgumentException">If the entity is null, or the given column is not found on the entity.</exception>
		public static object GetValue(Entity entity, string columnName)
		{
			if (entity == null)
				throw new ArgumentException(String.Format("Cannot retrieve property information for column \"{0}\" because no entity was provided.", columnName));

			if (!entity.Columns.Contains(columnName))
				throw new ArgumentException(String.Format("Column \"{0}\" not found on entity type \"{1}\".", columnName, entity.EntityTypeName));

			return entity[columnName].Value;
		}

		/// <summary>
		/// Gets the value of the given column for the entity being changed.
		/// </summary>
		/// <param name="columnName">The name of the column to retrieve.</param>
		/// <returns>The value of the column for the entity being changed.</returns>
		public object GetValue(string columnName)
		{
			return GetValue(FEntityChange.Entity, columnName);
		}

		/// <summary>
		/// Gets the original value of the given column for the given entity.
		/// </summary>
		/// <param name="entity">The entity to use for the retrieval.</param>
		/// <param name="columnName">The name of the column to retrieve.</param>
		/// <returns>The original value of the column for the entity.</returns>
		/// <exception cref="ArgumentException">If the entity is null, or the given column is not found on the entity.</exception>
		public static object GetOriginalValue(Entity entity, string columnName)
		{
			if (entity == null)
				throw new ArgumentException(String.Format("Cannot retrieve column information for property \"{0}\" because no entity was provided.", columnName));

			if (!entity.Columns.Contains(columnName))
				throw new ArgumentException(String.Format("Field \"{0}\" not found on entity type \"{1}\".", columnName, entity.EntityType.Name));

			return entity.Columns[columnName].OriginalValue;
		}

		/// <summary>
		/// Gets the original value of the given column for the entity being changed.
		/// </summary>
		/// <param name="columnName">The name of the column to retrieve.</param>
		/// <returns>The original value of the column for the entity being changed.</returns>
		public object GetOriginalValue(string columnName)
		{
			return GetOriginalValue(FEntityChange.Entity, columnName);
		}
	}
}